import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import main from '../main';
import { AppStore, StoreDetail, StoreRecordBaseParams, StoreRecordUpdateParams } from '../types';
import { cloneDeep } from '../utils/utils';
import { listClearTable } from './listReducer';
import { paginateClearTable } from './paginateReducer';

// 请求成功时自动传送的参数
interface SuccessParams<T> extends StoreRecordBaseParams<T> {
  detail: T;
}

// 请求失败时自动传送的参数
interface failureParams<T> extends StoreRecordBaseParams<T> {
  error: string;
}

const detailSlice = createSlice({
  name: 'detail',
  initialState: {} as StoreDetail<any>,
  reducers: {
    detailRequest: <T>(state: StoreDetail<T>, action: PayloadAction<StoreRecordBaseParams<T>>) => {
      let key = createStoreDetailKey(action.payload);
      if (!state[key] || action.payload.forceRefresh) {
        state[key] = {
          fetching: false,
          loaded: false,
          model: action.payload.model,
          table: action.payload.table,
          id: action.payload.id,
        };
      }
      state[key].fetching = true;
      return state;
    },
    detailUpdate: <T>(state: StoreDetail<T>, action: PayloadAction<StoreRecordUpdateParams<T>>) => {
      if (action.payload.id && action.payload.id !== 'create') {
        let key = createStoreDetailKey(action.payload);
        if (!state[key]) {
          state[key] = {
            fetching: false,
            loaded: false,
            model: action.payload.model,
            table: action.payload.table,
            id: action.payload.id,
          };
        }
        state[key].fetching = true;
      }
      return state;
    },
    detailRemove: <T>(state: StoreDetail<T>, action: PayloadAction<StoreRecordBaseParams<T>>) => {
      return state;
    },
    detailRemoveSuccess: <T>(state: StoreDetail<T>, action: PayloadAction<StoreRecordBaseParams<T>>) => {
      let key = createStoreDetailKey(action.payload);
      delete state[key];
      return state;
    },
    detailSuccess: <T>(state: StoreDetail<T>, action: PayloadAction<SuccessParams<T>>) => {
      let key = createStoreDetailKey(action.payload);
      if (!state[key]) {
        state[key] = {
          fetching: false,
          loaded: false,
          model: action.payload.model,
          table: action.payload.table,
          id: action.payload.id,
        };
      }
      state[key].fetching = false;
      state[key].loaded = true;
      state[key].error = undefined;
      state[key].result = action.payload.detail;
      return state;
    },
    detailFailure: <T>(state: StoreDetail<T>, action: PayloadAction<failureParams<T>>) => {
      let key = createStoreDetailKey(action.payload);
      if (!state[key]) {
        state[key] = {
          fetching: false,
          loaded: false,
          model: action.payload.model,
          table: action.payload.table,
          id: action.payload.id,
        };
      }
      state[key].fetching = false;
      state[key].loaded = true;
      state[key].error = action.payload.error;
      return state;
    },
    detailClear: (state) => {
      return {};
    },
  },
});

export const {
  detailRequest,
  detailSuccess,
  detailRemove,
  detailRemoveSuccess,
  detailUpdate,
  detailFailure,
  detailClear,
} = detailSlice.actions;
export default detailSlice.reducer;

export const detailEffect = {
  'detail/clear': (store: ToolkitStore<AppStore>) => store.dispatch(detailClear()),
  'detail/detailRequest': async <T>(store: ToolkitStore<AppStore>, action: PayloadAction<StoreRecordBaseParams<T>>) => {
    let key = createStoreDetailKey(action.payload);
    let detail = store.getState().detail?.[key];
    if (detail?.loaded && detail?.result && !detail.error && !action.payload.forceRefresh) {
      store.dispatch(detailSuccess({ ...action.payload, detail: cloneDeep(detail.result) }));
      if (action.payload.callback) {
        action.payload.callback!(200, '获取成功');
      }
    } else {
      let code: number | undefined;
      let msg: string | undefined;
      try {
        let headers: any;
        let url = `${action.payload.model}/${action.payload.table}/${action.payload.id}`;

        // 请求接口 处理数据 并发送成功或失败action
        let res = await main.api.get(url, { headers });
        let data = res.data;
        code = data.code;
        if (data.code === 200) {
          msg = data.msg ?? '获取成功';
          store.dispatch(
            detailSuccess({
              model: action.payload.model,
              table: action.payload.table,
              id: action.payload.id,
              detail: data.data,
            })
          );
        } else {
          msg = data.msg || '获取失败';
          store.dispatch(
            detailFailure({
              model: action.payload.model,
              table: action.payload.table,
              id: action.payload.id,
              error: msg!,
            })
          );
        }
      } catch (e: any) {
        code = 500;
        msg = e.message ?? '获取失败';
        store.dispatch(
          detailFailure({
            model: action.payload.model,
            table: action.payload.table,
            id: action.payload.id,
            error: msg!,
          })
        );
      } finally {
        if (action.payload.callback) {
          action.payload.callback!(code, msg);
        }
      }
    }
  },
  'detail/detailUpdate': async <T>(
    store: ToolkitStore<AppStore>,
    action: PayloadAction<StoreRecordUpdateParams<T>>
  ) => {
    let code: number | undefined;
    let msg: string | undefined;
    let record: T = action.payload.detail;
    try {
      let fn = action.payload.id === 'create' ? main.api.post : main.api.put;
      let res = await fn(`${action.payload.model}/${action.payload.table}`, action.payload.detail);
      let data = res.data;
      code = data.code;
      record = data.data ?? record;
      if (data.code === 200) {
        msg = data.msg ?? '操作成功';
        if (!!data.data) {
          store.dispatch(
            detailSuccess({
              model: action.payload.model,
              table: action.payload.table,
              id: data.data.id,
              detail: data.data,
            })
          );
        }
        let id = data.data?.id ?? action.payload.id;
        if (id && id !== 'create' && action.payload.queryDetail !== false) {
          store.dispatch(
            detailRequest({ model: action.payload.model, table: action.payload.table, id, forceRefresh: true })
          );
        }
        store.dispatch(listClearTable({ model: action.payload.model, table: action.payload.table }));
        store.dispatch(
          paginateClearTable({
            model: action.payload.model,
            table: action.payload.table,
            pageNum: 1,
            pageSize: 1,
          })
        );
      } else {
        msg = data.msg ?? '操作失败';
        store.dispatch(
          detailFailure({
            model: action.payload.model,
            table: action.payload.table,
            id: action.payload.id ?? '',
            error: msg!,
          })
        );
      }
    } catch (e: any) {
      code = 500;
      msg = e.message ?? '操作失败';
      store.dispatch(
        detailFailure({
          model: action.payload.model,
          table: action.payload.table,
          id: action.payload.id ?? '',
          error: msg!,
        })
      );
    } finally {
      if (action.payload.callback) {
        action.payload.callback!(code, msg, record);
      }
    }
  },
  'detail/detailRemove': async (store: ToolkitStore<AppStore>, action: PayloadAction<StoreRecordBaseParams<any>>) => {
    let code: number | undefined;
    let msg: string | undefined;
    try {
      let res = await main.api.delete(`${action.payload.model}/${action.payload.table}/${action.payload.id}`);
      let data = res.data;
      code = data.code;
      if (data.code === 200) {
        msg = data.msg ?? '删除成功';
        store.dispatch(detailRemoveSuccess(action.payload));
      } else {
        msg = data.msg ?? '操作失败';
      }
    } catch (e: any) {
      code = 500;
      msg = e.message ?? '操作失败';
    } finally {
      if (action.payload.callback) {
        action.payload.callback!(code, msg);
      }
    }
  },
};

export function createStoreDetailKey(payload: StoreRecordBaseParams<any>) {
  let { model, table, id } = payload;
  let key = model + '/' + table + '/' + id;
  return key;
}
